package com.pan.framework.core.factory;

import com.pan.framework.anno.aop.MyAspect;
import com.pan.framework.anno.conf.MyBean;
import com.pan.framework.anno.conf.MyConfig;
import com.pan.framework.anno.controller.MyController;
import com.pan.framework.anno.dao.MyDao;
import com.pan.framework.anno.server.MyService;
import com.pan.framework.core.container.MyContainer;
import com.pan.framework.core.factory.scan.MyScan;
import com.pan.framework.entity.BeanInfo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;

public class MyFactory {


    private MyContainer container;
    private MyScan scan;

    public MyFactory( MyContainer container, MyScan scan) {
        this.scan = scan;
        this.container = container;
    }

    public void init(){
        HashSet<String> classNameSet = scan.getClassNameSet();
        for (String s : classNameSet) {
            try {
                create(s);
            } catch (Exception e) {
                System.out.println(s);
                throw new RuntimeException(e);
            }
        }
    }

    public void create(String className) throws Exception {
        ClassLoader myLoader = scan.getMyLoader();
        Class<?> clazz = myLoader.loadClass(className);
        String name = null;
        if (clazz.isAnnotationPresent(MyConfig.class)){
            configCreate(clazz);
            return;
        }

        if (clazz.isAnnotationPresent(MyDao.class)) {
            MyDao anno = clazz.getAnnotation(MyDao.class);
            name = anno.name();
        } else if (clazz.isAnnotationPresent(MyService.class)) {
            MyService anno = clazz.getAnnotation(MyService.class);
            name = anno.name();
        }else if (clazz.isAnnotationPresent(MyController.class)){
            MyController anno = clazz.getAnnotation(MyController.class);
            name = anno.name();
        } else if (clazz.isAnnotationPresent(MyAspect.class)) {
            MyAspect anno = clazz.getAnnotation(MyAspect.class);
            name = anno.name();
        }else {
            return;
        }
        BeanInfo bi = createBean(name, clazz);
        container.addBean(bi);

    }

    public BeanInfo createBean(String name,Class targetClazz){
        try {
            Constructor constructor = targetClazz.getConstructor();
            Object target = constructor.newInstance();
            return new BeanInfo(name,targetClazz,target);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void configCreate(Class c) throws Exception {
        Object configObj = c.getConstructor().newInstance();
        Method[] methods = c.getMethods();
        Arrays.stream(methods).filter(method -> method.isAnnotationPresent(MyBean.class))
                .forEach(method -> {
                    MyBean anno = method.getAnnotation(MyBean.class);
                    String name = anno.name();
                    Object target;
                    try {
                        target = method.invoke(configObj);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                    container.addBean(new BeanInfo(
                            name,
                            anno.getClass(),target
                    ));
                });
    }

}
